local tabletool = require "tabletool"
local timetool = require "timetool"
local msghelper = require "tablehelper"
local timer = require "timer"
local filelog = require "filelog"
local skynet = require "skynet"
local redisdao = require "redisdao"
local configdao = require "configdao"
local cluster = require "skynet.cluster"

local math_floor = math.floor
local math_ceil = math.ceil
local gang28 = require "gang28"

local json = require "cjson"
json.encode_sparse_array(true,1,1)

require "gang28enum"

local STOCK = 0.95

local gamelogic
local seatlogic
local dzlogic
local TableLogic = {}

--------------------

---获取节点配置
--包括服务费配置，奖池配置
--nodeconf = {tipconf,jackoptconf}
--
local function getnodeconf()
	local nodeconf = nil
	local result, data = redisdao.query_data(".gmmqredis", "get", "gane28node_conf")
    if result and data then
        nodeconf = json.decode(data)
    end
    return nodeconf or {}
end

-----------------------------------

function TableLogic.init(tablebase, conf)
	if conf == nil then
		filelog.sys_error("TableLogic.init conf = nil")
		return false
	end

	local table_data = tablebase.table_data
	gamelogic = tablebase.logic.gamelogic
	seatlogic = tablebase.logic.seatlogic

	table_data.idx = conf.idx
	table_data.state = ETableState.TABLE_STATE_WAIT_ALL_READY

	local seat
	local count = 1
    while count <= conf.max_player_num do
    	seat = {
    		index = 0,
			rid = 0,
			state = 0,  --该坐位玩家状态
			playerinfo = {
				rolename = "",
				logo = "",
				sex = 0,
				ip_addr = "",
				fangka = 0,
				diamond = 0,
			},
			is_tuoguan = EBOOL.FALSE,
			is_ready = EBOOL.FALSE,
			cdiamond = 0,
			diamond = 0,
    		ready_timer_id = -1, ----准备倒计时定时器
    		ready_to_time = 0,   ----准备到期时间
			cards = {},		     ----玩家手牌
    	}
    	seatlogic.init(seat, count)
    	table.insert(table_data.seats, seat) 
		count = count + 1
    end
	table_data.conf = tabletool.deepcopy(conf)
	table_data.playernum = 0 --玩家数量
	table_data.the_round = 1

	table_data.gamelogic = {table_data = nil,}
	gamelogic.init(tablebase)

	--[[获取最新彩池信息
	table_data.jackopt_info = {}
	if table_data.conf.room_type == ERoomType.ROOM_TYPE_DIAMOND then
	   TableLogic.register_jackopt(table_data)
	   TableLogic.get_jackoptinfo(table_data)
	end
	table_data.jacktip = TableLogic.get_jacktip(table_data)
	--]]
	return true
end

function TableLogic.checkallonline(table_data)
	local online = false
	for _, seat in ipairs(table_data.seats) do
		if seat.state ~= ESeatState.SEAT_STATE_NO_PLAYER then
			if not seat.is_disconnect then
				online = true
				break
			end
		end
	end
	if not online then
		if table_data.timer_id > 0 then
			timer.cleartimer(table_data.timer_id)
			table_data.timer_id = -1
		end
		for _, seat in ipairs(table_data.seats) do
			if seat.state ~= ESeatState.SEAT_STATE_NO_PLAYER then
				TableLogic.passive_standuptable(table_data, seat)
			end
		end
	end
	return online
end

function TableLogic.disconnect(table_data, request, seat)
	if not table_data.conf.match_type and (seat.state == ESeatState.SEAT_STATE_WAIT_READY or seat.state == ESeatState.SEAT_STATE_WATCHING) then
		TableLogic.passive_standuptable(table_data, seat)
	else
		if table_data.conf.room_type == ERoomType.ROOM_TYPE_SYSTEM then
			TableLogic.passive_standuptable(table_data, seat)
		else
			seat.is_tuoguan = EBOOL.TRUE
			seat.is_disconnect = true
			-- if TableLogic.checkallonline(table_data) then
				msghelper:sendmsg_to_alltableplayer("disconnectN", {
					rid = seat.rid,
					seat_index = seat.index,
					is_tuoguan = seat.is_tuoguan,
				})
			-- end
		end
	end
end

function TableLogic.clear(table_data)
	if table_data.timer_id > 0 then
		timer.cleartimer(table_data.timer_id)
		table_data.timer_id = -1
	end

	if table_data.delete_table_timer_id > 0 then
		timer.cleartimer(table_data.delete_table_timer_id)
		table_data.delete_table_timer_id = -1
	end

	for k,v in pairs(table_data) do
		table_data[k] = nil
	end
end

--[[
	seat: nil表示否， 非nil表示是
--]]
function TableLogic.entertable(table_data, rid, rdata, request, seat)
	if table_data.delete_table_timer_id and table_data.delete_table_timer_id > 0 then
		timer.cleartimer(table_data.delete_table_timer_id)
		table_data.delete_table_timer_id = -1
	end
	local rid = rid
	if seat then 
		if seat.is_tuoguan == EBOOL.TRUE then
			seat.is_tuoguan = EBOOL.FALSE
		end
	else
		if table_data.waits[rid] == nil then
			table_data.waits[rid] = {}
			table_data.waits[rid].playerinfo = {}
			table_data.cur_watch_playernum = table_data.cur_watch_playernum + 1			 
		end
		table_data.waits[rid].rid = rid
		table_data.waits[rid].playerinfo.rolename = rdata.info.rolename
		table_data.waits[rid].playerinfo.logo = rdata.info.rolename
		table_data.waits[rid].playerinfo.sex = rdata.info.sex
		if table_data.conf.match_type then
			table_data.waits[rid].waitreenter = 1
		end
	end
end

function TableLogic.reentertable(table_data, request, seat)
	msghelper:sendmsg_to_alltableplayer("disconnectN", {
		rid = seat.rid,
		seat_index = seat.index,
		is_tuoguan = EBOOL.FALSE,
	})

	if table_data.delete_table_timer_id and table_data.delete_table_timer_id > 0 then
		timer.cleartimer(table_data.delete_table_timer_id)
		table_data.delete_table_timer_id = -1
	end

	seat.sametabledata = request.countdata
	msghelper:sendmsg_to_alltableplayer("sametablecountN", {
		rid = seat.rid,
		countdata = seat.sametabledata
	})
end

function TableLogic.sitdowntable(table_data, rid, rdata, request, seat)
	if table_data.waits[rid] then
		table_data.waits[rid] = nil
		table_data.cur_watch_playernum = table_data.cur_watch_playernum - 1
	end
	if table_data.conf.match_type then
		seat.waitreenter = 1
	end

	seat.state = ESeatState.SEAT_STATE_WAIT_READY	--等待开始
	seat.rid = rid
	seat.playerinfo.rolename = rdata.info.rolename
	seat.playerinfo.logo = rdata.info.logo
	seat.playerinfo.sex = rdata.info.sex
	seat.playerinfo.ip_addr = rdata.info.ip_addr or ''
	seat.playerinfo.gps_addr = rdata.info.gps_addr or ''
	seat.playerinfo.fangka = rdata.money.fangka or 0
	seat.playerinfo.diamond = rdata.money.diamond or 0
	seat.playerinfo.coin = rdata.money.coin or 0
	seat.playerinfo.introduce = rdata.info.intro or ''
	seat.playerinfo.upvoted_num = rdata.info.upvoted_num or 0
	seat.playerinfo.downvoted_num = rdata.info.downvoted_num or 0
	seat.playerinfo.logoframe = rdata.info.logoframe
	seat.getfangka = rdata.money.fangka
	seat.diamond = rdata.money.diamond
	if table_data.conf.room_type == ERoomType.ROOM_TYPE_FRIEND then
		seat.diamond = 0
	elseif table_data.conf.room_type == ERoomType.ROOM_TYPE_SYSTEM then
		seat.diamond = 10000
	elseif table_data.conf.room_type == ERoomType.ROOM_TYPE_COIN then
		seat.diamond = rdata.money.coin
	end
	seat.cdiamond = seat.diamond

	-- 判读sitdown时，玩家是否处于中途进入
	if table_data.gaming then
		seat.state = ESeatState.SEAT_STATE_WATCHING
	else
		local outoftimermsg = {
			rid = seat.rid,
			seat_index = seat.index,
		}
		seat.ready_timer_id = timer.settimer(table_data.conf.ready_timeout*100, "doready", outoftimermsg)
		seat.ready_to_time = timetool.get_time() + table_data.conf.ready_timeout
	end

	local noticemsg = {
		rid = seat.rid,
		seat = {},
		player_info = {},
	}
	msghelper:copy_seatinfo(noticemsg.seat, seat)
	msghelper:copy_playerinfo(noticemsg.player_info, seat)
	msghelper:sendmsg_to_alltableplayer("sitdowntableN", noticemsg)
	if not table_data.gaming then
		local doreadyntcmsg = {
			rid = seat.rid,
			seat_index = seat.index,
			ready_to_time = seat.ready_to_time,
		}
		msghelper:sendmsg_to_alltableplayer("doreadyN", doreadyntcmsg)
	end

	if seat.is_tuoguan == EBOOL.TRUE then
		seat.is_tuoguan = EBOOL.FALSE
		--TO ADD 添加托管处理
	end
	if seat.rid > 0 and seat.rid < 100 then
		seat.is_tuoguan = EBOOL.TRUE
	end
	--增加桌子人数计数 
	table_data.sitdown_player_num = table_data.sitdown_player_num + 1
	table_data.playernum = table_data.playernum + 1
	if table_data.conf.room_type == ERoomType.ROOM_TYPE_DIAMOND then
		msghelper:sendmsg_to_roomsvr("update_table_player", 
			table_data.conf.idx,
			table_data.sitdown_player_num,
			table_data.conf.is_public,
			table_data.conf.match_type,
			table_data.conf.room_type
		)
	end

	-------------------------- robot --------------------------
	if rdata.info.level == 99 then
		seat.isrobot = true
		msghelper:sendmsg_to_roomsvr("change_robot_num", 1, table_data.conf.idx, table_data.conf.is_public, table_data.conf.match_type, table_data.conf.room_type)
		if table_data.sitdown_player_num == 1 and table_data.conf.create_user_rid == 101 then
			table_data.conf.name = seat.playerinfo.rolename .. "的房间"
	    	table_data.conf.rid = seat.rid
	    	table_data.conf.create_user_rid = rid
			table_data.conf.create_user_name = seat.playerinfo.rolename
	    	table_data.conf.create_user_logo = seat.playerinfo.logo
		end
	end
end

function TableLogic.gameready(table_data, request, seat)
	if seat.ready_timer_id and seat.ready_timer_id > 0  then ---取消准备倒计时
		timer.cleartimer(seat.ready_timer_id)
		seat.ready_timer_id = -1
		seat.ready_to_time = 0
	end

	seat.state = ESeatState.SEAT_STATE_WAIT_START
	seat.is_ready = EBOOL.TRUE
	local noticemsg = {
		rid = seat.rid,
		seat_index = seat.index,
		isready = 1,
		table_state = 0,
	}
	noticemsg.table_state = table_data.state
	msghelper:sendmsg_to_alltableplayer("gamereadyN", noticemsg)
	
	if table_data.conf.match_type and request.rid > 100 then -- and table_data.the_round > 1 then
		if table_data.playernum < 2 then
			local match_type = table_data.conf.match_type
			if table_data.conf.room_type == ERoomType.ROOM_TYPE_DIAMOND then
				skynet.call(".router","lua", "restart_matchtable", {request.rid}, match_type)
			elseif table_data.conf.room_type == ERoomType.ROOM_TYPE_COIN then
				skynet.call(".router","lua", "restart_coinmatchtable", {request.rid}, match_type)
			end
			TableLogic.passive_standuptable(table_data, seat)
			return
		end
	end

	if not table_data.gaming then
		TableLogic.startgame(table_data)
	end
end

function TableLogic.startgame(table_data)
	local isallready = true
	for k, v in ipairs(table_data.seats) do
		if v.state ~= ESeatState.SEAT_STATE_NO_PLAYER then
			if v.state ~= ESeatState.SEAT_STATE_WAIT_START then
				isallready = false
				break
			end
		end
	end
	filelog.debug("------ isallready ------", isallready)
	if not isallready then
		return
	end
	if TableLogic.is_canstartgame(table_data) then
		table_data.state = ETableState.TABLE_STATE_GAME_START
		table_data.gaming = true
		gamelogic.gamestart(table_data.gamelogic)
		filelog.warn("------ game start ------")
		return true
	else
		table_data.state = ETableState.TABLE_STATE_WAIT_ALL_READY
		return false
	end
end

--
--处理重新匹配
--
function TableLogic.restart_matchtable(table_data, rid)
	local rids = {}
	local ts = timetool.get_time()
	for k, v in ipairs(table_data.seats) do
		if v.rid > 100 and v.rid ~= (rid or 0) and v.state == ESeatState.SEAT_STATE_WAIT_START then
			if not v.ready_to_time or (v.ready_to_time == 0) or ts <= (v.ready_to_time - 1) then
				table.insert(rids, v.rid)
			end
		end
	end
	local match_type = table_data.conf.match_type
	if table_data.conf.room_type == ERoomType.ROOM_TYPE_DIAMOND then
		msghelper:callmsg_to_roomsvr("restart_matchtable", rids, match_type)
	elseif table_data.conf.room_type == ERoomType.ROOM_TYPE_COIN then
		msghelper:callmsg_to_roomsvr("restart_coinmatchtable", rids, match_type)
	end
	for _, rid in ipairs(rids) do
		local seat = TableLogic.get_seat_by_rid(table_data, rid)
		TableLogic.passive_standuptable(table_data, seat, nil, true)
	end
end

function TableLogic.leaveallplayers(table_data)
	for _, seat in ipairs(table_data.seats) do
		if seat.state ~= ESeatState.SEAT_STATE_NO_PLAYER then
			TableLogic.passive_standuptable(table_data, seat)
		end
	end
end

function TableLogic.leavetable(table_data, request, seat, re)
	local leavetablentc = {rid = request.rid}
	msghelper:sendmsg_to_alltableplayer("leavetableN", leavetablentc)
	msghelper:sendmsg_to_roomsvr("playerleavetable", request.rid, table_data.conf.game_room_type)
	if not table_data.conf.match_type and isrobot then
		pcall(cluster.send, "robotnode", ".router", "request_robot_leave", "gang28node", table_data.idx, {request.rid})
	end

	local delete = false
	if not table_data.conf.match_type then
		local isallrobot = true
		for _, seat in ipairs(table_data.seats) do
			if seat.state ~= ESeatState.SEAT_STATE_NO_PLAYER then
				if seat.rid > 100 and not seat.isrobot then
					isallrobot = false
				end
			end
		end
		if isallrobot then
			if TableLogic.is_onegameend(table_data) then	-- 游戏没结束
				delete = true
			end
		end
	end

	--匹配场
	if table_data.conf.match_type and table_data.playernum < 2 then	-- 匹配桌人数太少，退桌重新进匹配
		if TableLogic.is_onegameend(table_data) then	-- 游戏没结束不能进行重连
			--匹配场
			if not table_data.recycled and not re then
				table_data.recycled = true
				TableLogic.restart_matchtable(table_data, request.rid) --匹配场，将座位上玩家，加入匹配队列
			end
		end
	end

	local retain_time = table_data.conf.retain_time
	if table_data.playernum <= 0 or table_data.conf.room_type == ERoomType.ROOM_TYPE_SYSTEM then
		if not table_data.delete_table_timer_id or table_data.delete_table_timer_id == -1 then
			delete = true
		end
	end

	if delete then
		table_data.delete_table_timer_id = timer.settimer(retain_time*100, "delete_table")
		table_data.the_round = 1
		table_data.recycled = nil
		if table_data.timer_id and table_data.timer_id > 0 then
			timer.cleartimer(table_data.timer_id)
			table_data.timer_id = -1
		end
		for k,v in ipairs(table_data.seats or {}) do
			if v.state == ESeatState.SEAT_STATE_WAIT_READY then
				if v.ready_timer_id > 0 then
					timer.cleartimer(v.ready_timer_id)
					v.ready_timer_id = -1
				end
			end
		end
		return
	end
	if table_data.waits[request.rid] then
		table_data.waits[request.rid] = nil
		table_data.cur_watch_playernum = table_data.cur_watch_playernum - 1
	end
	if not table_data.gaming then
		if not table_data.conf.match_type then
			TableLogic.startgame(table_data)
		else
			if table_data.playernum >= 2 then
				TableLogic.startgame(table_data)
			end
		end
	end
end

function TableLogic.passive_standuptable(table_data, seat, reason, re)
	if not seat or seat.rid <= 0 or seat.standup then return end
	local rid = seat.rid
	table_data.sitdown_player_num = table_data.sitdown_player_num - 1
	table_data.playernum = table_data.playernum - 1
	if table_data.sitdown_player_num < 0 then
		table_data.sitdown_player_num = 0
		table_data.playernum = 0
		filelog.sys_error("passive_standuptable: sitdown_player_num < 0 !!!", table_data.sitdown_player_num)
	end
	if table_data.conf.room_type == ERoomType.ROOM_TYPE_DIAMOND then
		msghelper:sendmsg_to_roomsvr("update_table_player", 
			table_data.conf.idx,
			table_data.sitdown_player_num,
			table_data.conf.is_public,
			table_data.conf.match_type,
			table_data.conf.room_type
		)
	end
	if seat.isrobot then
		msghelper:sendmsg_to_roomsvr("change_robot_num", -1, table_data.conf.idx, table_data.conf.is_public, table_data.conf.match_type, table_data.conf.room_type)
	end
	local noticemsg = {
		rid = rid, 
		seat_index = seat.index,
		state = seat.state,
		reason = reason,
	}
	msghelper:sendmsg_to_alltableplayer("standuptableN", noticemsg) --同知座位上的人，某位玩家离
	seat.standup = true

	table_data.winner = nil

	--初始化座位数据
	seatlogic.clear_seat(seat)
	TableLogic.leavetable(table_data, {rid = rid}, seat, re)
end

function TableLogic.standuptable(table_data, request, seat)
	if not seat or seat.rid <= 0 then return end
	table_data.sitdown_player_num = table_data.sitdown_player_num - 1
	table_data.playernum = table_data.playernum - 1
	if table_data.sitdown_player_num < 0 then
		table_data.sitdown_player_num = 0
		table_data.playernum = 0
		filelog.sys_error("standuptable: sitdown_player_num < 0 !!!", table_data.sitdown_player_num)
	end
	if table_data.conf.room_type == ERoomType.ROOM_TYPE_DIAMOND then
		msghelper:sendmsg_to_roomsvr("update_table_player", 
			table_data.conf.idx,
			table_data.sitdown_player_num,
			table_data.conf.is_public,
			table_data.conf.match_type,
			table_data.conf.room_type
		)
	end
	if isrobot then
		msghelper:sendmsg_to_roomsvr("change_robot_num", -1, table_data.conf.idx, table_data.conf.is_public, table_data.conf.match_type, table_data.conf.room_type)
	end
	local noticemsg = {
		rid = seat.rid, 
		seat_index = seat.index,
		state = seat.state,
	}
	msghelper:sendmsg_to_alltableplayer("standuptableN", noticemsg)
	seat.standup = true
	-- if seat.index == table_data.winner then
		table_data.winner = nil
	-- end
	--初始化座位数据
	seatlogic.clear_seat(seat)
end

function TableLogic.call(table_data, seat, val)
	seat.call = val
	if val > 0 then
		table.insert(table_data.bankercallers[val], seat.index)
	end
	msghelper:sendmsg_to_alltableplayer("playercallresultN", {
		rid = seat.rid,
		seat_index = seat.index,
		value = val,
	})
	table_data.calledcount = table_data.calledcount + 1
	if table_data.calledcount >= table_data.playingnum then
		timer.cleartimer(table_data.timer_id)
		table_data.timer_id = -1
		table_data.waittotime = nil
		table_data.state = ETableState.TABLE_STATE_WAIT
		gamelogic.setbanker()
	end
end

function TableLogic.bets(table_data, seat, val)
	seat.bets = val
	msghelper:sendmsg_to_alltableplayer("playerbetresultN", {
		rid = seat.rid,
		seat_index = seat.index,
		value = val,
	})
	table_data.betedcount = table_data.betedcount + 1
	if table_data.betedcount >= table_data.playingnum - 1 then
		timer.cleartimer(table_data.timer_id)
		table_data.timer_id = -1
		table_data.waittotime = nil
		table_data.state = ETableState.TABLE_STATE_WAIT
		if table_data.conf.game_type == EGameType.GAME_TYPE_FREE then
			gamelogic.sendcards(2)
		elseif table_data.conf.game_type == EGameType.GAME_TYPE_SEECARD then
			gamelogic.sendcards(1)
		end
		table_data.timer_id = timer.settimer(5 * 100, "checkcards")
		table_data.waittotime = 5 + timetool.get_time()
		msghelper:sendmsg_to_alltableplayer("checkcardsN", {checktotime = timetool.get_time() + 5})
	end
end

function TableLogic.showcards(table_data, seat)
	seat.showcard = 1
	msghelper:sendmsg_to_alltableplayer("showcardsN", {
		rid = seat.rid,
		seat_index = seat.index,
		cards = seat.cards,
	})
	table_data.showcount = table_data.showcount + 1
	if table_data.showcount >= table_data.playingnum then
		timer.cleartimer(table_data.timer_id)
		table_data.timer_id = -1
		table_data.waittotime = nil
		gamelogic.checkcards()
	end
end

function TableLogic.changediamond(table_data, seat_index, val)
	local seat = table_data.seats[seat_index]
	if seat then
		seat.diamond = seat.diamond + val
		table_data.tmpdiamond = table_data.tmpdiamond or {}
		table_data.tmpdiamond[seat.rid] = {
			rolename = seat.playerinfo.rolename,
			beginvalue = math.floor(seat.cdiamond),
			value = math.floor(seat.diamond - seat.cdiamond),
			endvalue = math.floor(seat.diamond),
		}
		if table_data.conf.room_type == ERoomType.ROOM_TYPE_DIAMOND then
			msghelper:sendmsg_to_alltableplayer("moneychangeN", {money_change_list = {{
				rid = seat.rid,
				money_type = EMoneyType.DIAMOND,
				value = seat.diamond,
			}}})

			--[[判断是否触发奖池
			local openjackplayers = {}
			local card_type = gang28.cardtype(seat.cards)
			local card_val = gang28.cardvalue(seat.cards)
			if card_type == ECardType.CARD_TYPE_PAIR then
				if card_val == 10 then    --3A单独算
					card_type = ECardType.CARD_TYPE_PAIR_10
				end
			end
			if TableLogic.check_cangetjack(card_type) then
				table_data.openjack_players = table_data.openjack_players or {}
				if not table_data.openjack_players[seat.rid] then
					local item = { 
						rid = seat.rid, 
						rolename = seat.playerinfo.rolename, 
						card_type = card_type, 
						table_type = table_data.conf.room_type,
						base_score = table_data.conf.base_score,
						beginvalue = seat.cdiamond,
					}
					table.insert(openjackplayers, item)
					TableLogic.handle_openjackpot(table_data, openjackplayers)
					table_data.openjack_players[seat.rid] = true
				end
			end
			--]]
		elseif table_data.conf.room_type == ERoomType.ROOM_TYPE_COIN then
			msghelper:sendmsg_to_alltableplayer("moneychangeN", {money_change_list = {{
				rid = seat.rid,
				money_type = EMoneyType.COIN,
				value = seat.diamond,
			}}})
		end
	end
end

--
--  改变金钱
--
function TableLogic.change_money(table_data, rid, itemvalues)
    local records = table_data.gamerecords
	if type(records) ~= type{} or not records.logname then
		filelog.sys_error("game_sub_diamond error: no records")
		return false
	end

	-- local comment = {
	-- 	table_uuid = records.table_uuid,
	-- 	table_tradeid = skynet.call(".nodeuuid", "lua", "getuuid"),
	-- 	start_time = records.start_time or '0',
	-- 	replay_logname = records.logname,
	-- }
    local status,result = skynet.pcall(cluster.call, "gatenode", ".router", "change_money", rid, itemvalues, comment)
    if not status then
        filelog.sys_error("change_money call failed!")
        return false
	end
    return result, comment
end

function TableLogic.save_actions(table_data, action, cards)
	local records = table_data.gamerecords
	if type(records) ~= type{} or not records.logname then
		filelog.sys_error("save actions error: no records", records, action, cards)
		return
	end
	local logstr = ''
	if not action then
		logstr = logstr
			.. (records.table_uuid or '0') .. '\n'
			.. table_data.conf.room_type .. '\n'
			.. (records.tip or '0') .. '\n'
			.. (records.conf or "{}") .. '\n'
			.. (records.start_time or '0') .. '\n'
			.. (records.playerdata or '{}') .. '\n'
			.. (records.initcards or "{}") .. '\n'				-- 初始牌堆
			.. (records.handcards or "{}")
	else
		if type(action) == type{} then
			logstr = logstr .. json.encode(action)
		elseif action == "end" then
			logstr = logstr .. action
		else
			logstr = logstr .. action .. '_'
				.. "cards:" .. (json.encode(cards or {}))
		end
	end
	-- filelog.info("logstr = ", logstr)
	skynet.call(".tablereplaylog", "lua", "write", records.logname, logstr)
end

function TableLogic.caclate_tip(table_data)
	local nodeconf = getnodeconf()
    local tipconf = nodeconf.tipconf
    if not tipconf or not tipconf[table_data.conf.room_type]
    or not tipconf[table_data.conf.room_type][table_data.conf.game_type] then
        local base_cost = table_data.conf.base_score
		if table_data.conf.room_type == ERoomType.ROOM_TYPE_DIAMOND then
			if table_data.conf.game_type ==  EGameType.GAME_TYPE_FREE then
				table_data.tip = math_ceil(table_data.conf.base_score*0.5)
			else
				table_data.tip = math_ceil(table_data.conf.base_score*0.3)
			end
            return table_data.tip
		elseif table_data.conf.room_type == ERoomType.ROOM_TYPE_COIN then
			if table_data.conf.game_type ==  EGameType.GAME_TYPE_FREE then
				table_data.tip = 10 	-- math_ceil(table_data.conf.base_score*0.5)
			else
				table_data.tip = 10 	-- math_ceil(table_data.conf.base_score*0.3)
			end
            return table_data.tip
        end 
    end

	local tip_percent = tipconf[table_data.conf.room_type][table_data.conf.game_type]
	if tip_percent and tonumber(tip_percent) then
		tip_percent = tonumber(tip_percent)
		table_data.tip = math.floor(table_data.conf.base_score * tip_percent)
		return table_data.tip
	end
	table_data.tip = 0
    return 0
end

function TableLogic.pay_tip(table_data)
	local base_cost = table_data.conf.base_score
	local data = {}
	local tip = table_data.tip
	if tip <= 0 then return end
	for _, seat in ipairs(table_data.seats) do
		if seat.state ~= ESeatState.SEAT_STATE_NO_PLAYER and seat.state ~= ESeatState.SEAT_STATE_WATCHING then
			data[seat.rid] = {
				beginvalue = seat.cdiamond,
				value = tip,
				endvalue = 0,
			}
			seat.diamond = seat.diamond - tip
			seat.cdiamond = seat.cdiamond - tip
			if table_data.conf.room_type == ERoomType.ROOM_TYPE_DIAMOND then
				seat.playerinfo.diamond = seat.diamond
			elseif table_data.conf.room_type == ERoomType.ROOM_TYPE_COIN then
				seat.playerinfo.coin = seat.diamond
			end
			data[seat.rid].endvalue = seat.cdiamond
		end
	end
	local commnet = {
		table_uuid = table_data.gamerecords.table_uuid,		
		table_tradeid = skynet.call(".nodeuuid", "lua", "getuuid"),
		start_time = table_data.gamerecords.start_time,
	}
	if table_data.conf.room_type == ERoomType.ROOM_TYPE_DIAMOND then
		msghelper:sendmsg_to_gatesvr("game_sub_fuwufei", data, commnet)
	elseif table_data.conf.room_type == ERoomType.ROOM_TYPE_COIN then
		msghelper:sendmsg_to_gatesvr("game_sub_coin", data, commnet)
	end
	-- msghelper:sendmsg_to_alltableplayer("paytipN", {
	-- 	tip = tip,
	-- })
end

function TableLogic.pay_roomcard(table_data)
	local data = {}
	for _, seat in ipairs(table_data.seats) do
		if seat.state ~= ESeatState.SEAT_STATE_NO_PLAYER then
			seat.playerinfo.fangka = seat.playerinfo.fangka - seat.fangka
			data[seat.rid] = {
				value = seat.fangka * 100,
			}
		end
	end
	local commnet = {
		table_uuid = skynet.getenv("nodename") .. "_" .. table_data.conf.table_code .. "_" .. table_data.conf.idx .. "_" .. timetool.get_time(),		
		table_tradeid = skynet.call(".nodeuuid", "lua", "getuuid"),	
	}
	msghelper:sendmsg_to_gatesvr("game_sub_fangka", data, commnet)
end

function TableLogic.change_all_player_money(table_data)
	if not table_data.tmpdiamond then
		filelog.sys_error("no data for changing player money,is somewhere error???")
		return
	end

	local records = table_data.gamerecords
	if type(records) ~= type{} or not records.logname then
		filelog.sys_error("change all player money error: no records")
		return
	end

	local commnet = {
		table_uuid = records.table_uuid,
		table_tradeid = skynet.call(".nodeuuid", "lua", "getuuid"),
		start_time = records.start_time or '0',
		replay_logname = records.logname,
	}

	filelog.trace("change all player money: ", table_data.tmpdiamond, commnet)
	local val = 0
	for k, v in pairs(table_data.tmpdiamond) do
		val = val + v.value
	end
	if val ~= 0 then
		filelog.error("diamond balance error!")
	end

	if table_data.conf.room_type == ERoomType.ROOM_TYPE_DIAMOND then
		msghelper:sendmsg_to_gatesvr("game_balance_diamond", table_data.tmpdiamond, commnet)
	elseif table_data.conf.room_type == ERoomType.ROOM_TYPE_COIN then
		msghelper:sendmsg_to_gatesvr("game_balance_coin_diamond", table_data.tmpdiamond, commnet)
	end
end

--根据指定桌位号获得一张空座位
function TableLogic.get_emptyseat_by_index(table_data)
	for index, seat in pairs(table_data.seats) do
		if seatlogic.is_empty(seat) then
			return seat
		end
	end
	return nil
end

function TableLogic.get_seat_by_rid(table_data, rid)
	for index, seat in pairs(table_data.seats) do
		if rid == seat.rid then
			return seat
		end
	end
	return nil
end

--判断桌子是否满了
function TableLogic.is_full(table_data)
	return table_data.sitdown_player_num >= table_data.conf.max_player_num
end

--判断当前是否能够开始游戏
function TableLogic.is_canstartgame(table_data)
	return table_data.sitdown_player_num >= table_data.conf.min_player_num
end

--判断当前局是否已经结束游戏
function TableLogic.is_onegameend(table_data)
	return (not table_data.gaming)
end

--[[
function TableLogic.get_jacktip(table_data)
    local room_type = table_data.conf.room_type
    local game_type = table_data.conf.game_type
	local base_cost = table_data.conf.base_score

    local nodeconf = getnodeconf()
    local jackoptconf = nodeconf.jackoptconf
	if not jackoptconf or not jackoptconf.chargeconf 
		or not jackoptconf.chargeconf[room_type]
		or not jackoptconf.chargeconf[room_type][game_type] then 
        return 0
    end

    local jackpoint = jackoptconf.chargeconf[room_type][game_type]
    return math.floor(base_cost * jackpoint)
end

---扣除奖池抽水
function TableLogic.pay_jackopt(table_data)
	local data = {}
	
	local jacktip = TableLogic.get_jacktip(table_data)
    jacktip = (jacktip >= 0 and jacktip) or 0
	table_data.jacktip = jacktip

    local jacktips = {}
	for _, seat in ipairs(table_data.seats) do
		if seat.state == ESeatState.SEAT_STATE_PLAYING then
			data[seat.rid] = {
				beginvalue = seat.cdiamond,
				value = jacktip,
				endvalue = 0,
			}
			seat.diamond = seat.diamond - jacktip
			seat.cdiamond = seat.cdiamond - jacktip
			data[seat.rid].endvalue = seat.cdiamond

            local jacktipsobj = { 
                rid = seat.rid, 
                jacktip = jacktip, 
                money_type = EMoneyType.DIAMOND, 
			    beginvalue = data[seat.rid].beginvalue, 
			    endvalue = data[seat.rid].endvalue
			}
            if table_data.conf.room_type == ERoomType.ROOM_TYPE_DIAMOND then
				seat.playerinfo.diamond = seat.diamond
			elseif table_data.conf.room_type == ERoomType.ROOM_TYPE_COIN then
				seat.playerinfo.coin = seat.diamond
                jacktipsobj.money_type = EMoneyType.COIN
			end
            table.insert(jacktips, jacktipsobj)
		end
	end
	local commnet = {
		table_uuid = table_data.gamerecords.table_uuid,		
		table_tradeid = skynet.call(".nodeuuid", "lua", "getuuid"),
		start_time = table_data.gamerecords.start_time,
	}
	if table_data.conf.room_type == ERoomType.ROOM_TYPE_DIAMOND then
		if not msghelper:callmsg_to_gatesvr("jackpot_sub_diamond", data, commnet) then
			filelog.sys_error("pay_jacktip serious error: ", data, commnet)
		end
	elseif table_data.conf.room_type == ERoomType.ROOM_TYPE_COIN then
		msghelper:sendmsg_to_gatesvr("jackpot_sub_coin", data, commnet)
	end
	table_data.gamerecords.jacktip = jacktip

	---更新奖池货币到总奖池
	skynet.send(".jackpotmgr", "lua", "updatejackoptnum", jacktips, commnet)
end

----获得奖池信息到牌桌
function TableLogic.get_jackoptinfo(table_data)
	local result = skynet.call(".jackpotmgr", "lua", "getjackInfo", table_data.conf.base_score)
	if next(result) ~= nil then
		if result.jackopt_num and result.money_type then
			if table_data.jackopt_info == nil then table_data.jackopt_info = {} end
			table_data.jackopt_info.jackopt_num = result.jackopt_num
			table_data.jackopt_info.money_type = result.money_type
		end
	end
end

---向奖池服务注册牌桌,奖池变化和分配时通知牌桌
function TableLogic.register_jackopt(table_data)
	skynet.send(".jackpotmgr", "lua", "register_table", table_data.conf.base_score, table_data.conf.table_code, skynet.self())
end

---从奖池服务里注销牌桌
function TableLogic.unregister_jackopt(table_data)
	skynet.send(".jackpotmgr", "lua", "unregister_table", table_data.conf.base_score, table_data.conf.table_code)
end

function TableLogic.get_horseracelamp(rolename, card_type, get_num, moneyname, cards)
	local horseconf = configdao.get_common_conf("horselampconf")
	if not horseconf or not horseconf[1] then return end
	local formatname = string.format(LAMPCOLOR.GREEN, rolename)
	local formatnum = string.format(LAMPCOLOR.RED, tostring(get_num))
    local formatmoney = string.format(LAMPCOLOR.RED, moneyname)
    local cardtypename = CardTypeName[card_type]
    local formatcardtype = string.format(LAMPCOLOR.GOLD,cardtypename)
	local horsestring = string.format(horseconf[1], formatname, EGameName, formatcardtype, formatnum, formatmoney)
	local msg = {
		content = horsestring,
	}
	return msg
end

function TableLogic.send_horseracelamp(msg)
	local result = cluster.send("gatenode", ".router", "addpopmessage", msg)
end

function TableLogic.check_cangetjack(card_type)
	local canget = false
	local nodeconf = getnodeconf()
    local jackoptconf = nodeconf.tipconf
	if not jackoptconf or not jackoptconf.getjackopt then return canget end
	for cardtype in pairs(jackoptconf.getjackopt) do
		if tonumber(cardtype) == card_type then canget = true break end
	end
	return canget
end

--处理开奖
function TableLogic.handle_openjackpot(table_data, getjackplayers)
    if not getjackplayers or #getjackplayers < 1 then
        return
    end

	local moneyname = "金豆"
    if table_data.conf.room_type == ERoomType.ROOM_TYPE_COIN then
        moneyname = "金币"
    end

    local data = {}
    local commnet = {
	    table_uuid = table_data.gamerecords.table_uuid,		
	    table_tradeid = skynet.call(".nodeuuid", "lua", "getuuid"),
	    start_time = table_data.gamerecords.start_time,
    }
	local jackplayers = skynet.call(".jackpotmgr", "lua", "openjackopt", getjackplayers, table_data.conf.table_code, commnet)
	if jackplayers and next(jackplayers) ~= nil then
		for index, jackplayer in ipairs(jackplayers) do
            local changevalue = jackplayer.getoptnum
            local seat = TableLogic.get_seat_by_rid(table_data, jackplayer.rid)
		    data[seat.rid] = {
			    beginvalue = seat.cdiamond,
			    value = changevalue,
			    endvalue = 0,
                rolename = seat.playerinfo.rolename,
		    }
		    seat.diamond = seat.diamond + changevalue
		    seat.cdiamond = seat.cdiamond + changevalue
		    if jackplayer.money_type == EMoneyType.DIAMOND then
			    seat.playerinfo.diamond = seat.diamond
		    elseif jackplayer.money_type == EMoneyType.COIN then
			    seat.playerinfo.coin = seat.diamond
		    end
		    data[seat.rid].endvalue = seat.cdiamond
            --通知客户端
            local noticemsg = {
			    rid = seat.rid,
			    card_type = jackplayer.card_type,
			    getjackopt_num = jackplayer.getoptnum,
		    }
            msghelper:sendmsg_to_tableplayer(seat, "getjackoptN", noticemsg)
            local msg = TableLogic.get_horseracelamp(seat.playerinfo.rolename, jackplayer.card_type, jackplayer.getoptnum, seat.cards)
            table.insert(table_data.horseracelamps,msg)
        end

        if table_data.conf.room_type == ERoomType.ROOM_TYPE_DIAMOND then
	        msghelper:callmsg_to_gatesvr("open_jackpot_diamond", "gang28", data, commnet)
        elseif table_data.conf.room_type == ERoomType.ROOM_TYPE_COIN then
	        msghelper:sendmsg_to_gatesvr("open_jackpot_coin", "gang28", data, commnet)
        end
	end
end

--保存牌型记录
function TableLogic.save_cardtype_record(gamemode, card_types, playernum)
    local record_list = nil
    local result, data = redisdao.query_data(".gmmqredis", "hget", "gang28_cardtype_record", gamemode)
    if result and data then
        record_list = json.decode(data)
    end

    record_list = record_list or {}
    if card_types then
        for _,card_type in pairs(card_types) do
            record_list[tostring(card_type)] = (record_list[tostring(card_type)] or 0) + 1
        end
    end
    if playernum then
        record_list.total_count = (record_list.total_count or 0) + playernum
    end
    redisdao.query_data(".gmmqredis", "hset", "gang28_cardtype_record", gamemode, json.encode(record_list))
end
--]]

return TableLogic
